package com.wx.spring.mvc.servlet;

import com.wx.spring.constant.BasicDataType;
import com.wx.spring.exception.SpringMvcException;
import com.wx.spring.ioc.IocContainer;
import com.wx.spring.ioc.factory.ApplicationContext;
import com.wx.spring.ioc.factory.BeanFactory;
import com.wx.spring.ioc.factory.InitBean;
import com.wx.spring.mvc.*;
import com.wx.spring.utils.MyStringUtils;
import lombok.extern.slf4j.Slf4j;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * @ClassName MyDispatcherServlet
 * @Author wx
 * @Description 控制器
 * @Date 2019-01-31-18:58
 */
@Slf4j
@WebServlet(name = "MyDispatcherServlet")
public class MyDispatcherServlet extends HttpServlet{
    private final static String SERVLET_CONTEXT_HANDLER_MAPPING_KEY = "handlerMapping";

    /**
     * @methodName: init
     * @author: wx
     * @description: 初始化
     * @param 
     * @date: 2019/1/31
     * @return: void
     */
    @Override
    public void init() throws ServletException {
        InitBean initBean = new InitBean();
        initBean.initBeans();
        //根据bean容器中注册的bean 进行绑定路径与方法
        Map<String, Method> handlerMapping = Handler.bindingRequestMapping(IocContainer.getBeanMap());
        //获取ServletContext上下文
        ServletContext servletContext = getServletContext();
        servletContext.setAttribute(SERVLET_CONTEXT_HANDLER_MAPPING_KEY, handlerMapping);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
       doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req, resp);
        } catch (InvocationTargetException e) {
            log.error("控制器处理异常");
            e.printStackTrace();
            throw new SpringMvcException("控制器处理异常");
        }catch (IllegalAccessException e1) {
            log.error("控制器处理异常");
            e1.printStackTrace();
            throw new SpringMvcException("控制器处理异常");
        }catch (InstantiationException e2) {
            log.error("控制器处理异常");
            e2.printStackTrace();
            throw new SpringMvcException("控制器处理异常");
        }
    }

    /**
     * @methodName: doDispatch
     * @author: wx
     * @description: 接收到请求后转发到相应的方法上
     * @param request
     * @param response
     * @date: 2019/1/31
     * @return: void
     */
    private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws IOException, InvocationTargetException, IllegalAccessException, InstantiationException, ServletException {
        //获取请求路径
        String url = request.getServletPath();
        if (MyStringUtils.isBlank(url)){
            notPage(request,response);
            return;
        }
        //spring上下文获取对象工厂
        BeanFactory applicationContext = new ApplicationContext();
        //获取servletContext上下文
        ServletContext servletContext = getServletContext();
        //获取扫描controller注解后url和方法绑定的mapping，也就是handlerMapping
        Map<String, Method> bindingRequestMapping =
                (Map<String, Method>) servletContext.getAttribute(SERVLET_CONTEXT_HANDLER_MAPPING_KEY);

        //获取请求路径对应的方法
        Method method = bindingRequestMapping.get(url);
        if (method == null){
            notPage(request,response);
            return;
        }
        //获取绑定参数的数据集合
        List<Object> resultParameters = Binding.bingdingMethodParamters(method, request);
        //获取方法返回值
        Class<?> returnType = method.getReturnType();
        //获取springmvc.xml中配置的视图解析器
        ViewResolver viewResolver = (ViewResolver) applicationContext.getBean("viewResolver");
        String prefix = viewResolver.getPrefix();
        String suffix = viewResolver.getSuffix();
        //获取bean容器中获取对象(getDeclaringClass获取该方法所属类的class)
        Object bean = applicationContext.getBean(method.getDeclaringClass());
        //如果返回类型是MyModelAndView
        if (Objects.equals(MyModelAndView.class.getSimpleName(),returnType.getSimpleName())){
            //执行controller方法 返回MyModelAndView
            MyModelAndView myModelAndView = (MyModelAndView) method.invoke(bean, resultParameters.toArray());
            //绑定Model数据到request
            BindingRequestAndModel.bindingRequestAndModel(myModelAndView,request);
            String returnViewName = myModelAndView.getView();
            if (MyStringUtils.isBlank(returnViewName)){
                notPage(request,response);
                return;
            }
            String resultAddress = prefix + returnViewName + suffix;
            System.out.println(resultAddress);
            request.getRequestDispatcher(resultAddress).forward(request,response);
        }else if (Objects.equals(BasicDataType.STRING,returnType.getSimpleName())){
            //执行controller方法 返回MyModelAndView
            String returnViewName = (String) method.invoke(bean, resultParameters.toArray());
            if (MyStringUtils.isBlank(returnViewName)){
                notPage(request,response);
                return;
            }
            String resultAddress = prefix + returnViewName + suffix;
            System.out.println(resultAddress);
            request.getRequestDispatcher(resultAddress).forward(request,response);
        } else {
            //执行controller方法
            method.invoke(bean, resultParameters.toArray());
        }

    }

    /**
     * @methodName: notPage
     * @author: wx
     * @description: 跳转404
     * @param request
     * @param response
     * @date: 2019/2/1
     * @return: void
     */
    private void notPage(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.getRequestDispatcher("404.jsp").forward(request,response);
    }
}
